package handlers

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	alertv1 "github.com/devops-smartbot/devops-smartbot/proto/gen/go/alert/v1"
	aiv1 "github.com/devops-smartbot/devops-smartbot/proto/gen/go/ai/v1"
	ticketv1 "github.com/devops-smartbot/devops-smartbot/proto/gen/go/ticket/v1"
	"github.com/devops-smartbot/devops-smartbot/services/api-gateway/internal/ai"
	"github.com/devops-smartbot/devops-smartbot/services/api-gateway/internal/clients"
	"github.com/devops-smartbot/devops-smartbot/services/api-gateway/internal/models"
	"github.com/devops-smartbot/devops-smartbot/services/api-gateway/internal/repository"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

type AIHandler struct {
	aiClient       clients.AIClient
	alertClient    clients.AlertClient
	ticketClient   clients.TicketClient
	analysisRepo   repository.AnalysisRepository
	openaiClient   *ai.OpenAIClient
}

func NewAIHandler(aiClient clients.AIClient, alertClient clients.AlertClient, ticketClient clients.TicketClient, analysisRepo repository.AnalysisRepository) *AIHandler {
	// Initialize OpenAI client with your credentials
	openaiClient := ai.NewOpenAIClient(
		"https://tbnx.plus7.plus/v1",
		"sk-yH4qHNYVp1kXGa8B97Ia2lysOVoyDaefq23bZOYNY21hlCin",
	)

	return &AIHandler{
		aiClient:     aiClient,
		alertClient:  alertClient,
		ticketClient: ticketClient,
		analysisRepo: analysisRepo,
		openaiClient: openaiClient,
	}
}

// AnalyzeAlert handles POST /api/ai/analyze
func (h *AIHandler) AnalyzeAlert(c *gin.Context) {
	var req struct {
		Provider string            `json:"provider"`
		Prompt   string            `json:"prompt"`
		Metadata map[string]string `json:"metadata"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Set default provider if not specified
	if req.Provider == "" {
		req.Provider = "openai"
	}

	grpcReq := &aiv1.AnalyzeRequest{
		Provider: req.Provider,
		Prompt:   req.Prompt,
		Metadata: req.Metadata,
	}

	resp, err := h.aiClient.Analyze(c.Request.Context(), grpcReq)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"output":     resp.Output,
		"cost":       resp.Cost,
		"latencyMs":  resp.LatencyMs,
		"metadata":   resp.Metadata,
	})
}

// AnalyzeAlertById handles POST /api/alerts/:id/analyze
func (h *AIHandler) AnalyzeAlertById(c *gin.Context) {
	alertID := c.Param("id")

	// Get alert details
	alert, err := h.alertClient.GetAlert(c.Request.Context(), &alertv1.GetAlertRequest{Id: alertID})
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get alert: " + err.Error()})
		return
	}
	if alert == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Alert not found"})
		return
	}

	// Build analysis prompt
	prompt := fmt.Sprintf(`Analyze this DevOps alert and provide detailed insights:

Alert Name: %s
Severity: %s
Status: %s
Instance: %s
Description: %s

Please provide:
1. A brief summary of the alert
2. Root cause analysis
3. Recommended actions (list 3-5 specific steps)
4. Confidence level (0-1)
5. Relevant tags

Format your response as JSON with this structure:
{
  "summary": "brief summary",
  "rootCause": "root cause analysis",
  "suggestions": ["action 1", "action 2", "action 3"],
  "confidence": 0.85,
  "tags": ["tag1", "tag2"]
}`, alert.Name, alert.Severity, alert.Status, alert.Instance, alert.Description)

	// Call AI service
	grpcReq := &aiv1.AnalyzeRequest{
		Provider: "openai",
		Prompt:   prompt,
		Metadata: map[string]string{
			"alert_id":    alertID,
			"alert_name":  alert.Name,
			"severity":    alert.Severity,
		},
	}

	resp, err := h.aiClient.Analyze(c.Request.Context(), grpcReq)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "AI analysis failed: " + err.Error()})
		return
	}

	// Try to parse AI response as JSON
	var aiAnalysis map[string]interface{}
	if err := json.Unmarshal([]byte(resp.Output), &aiAnalysis); err != nil {
		// If not JSON, return as raw text
		aiAnalysis = map[string]interface{}{
			"summary":    resp.Output,
			"rootCause":  "Unable to parse structured analysis",
			"suggestions": []string{},
			"confidence": 0.5,
			"tags":       []string{},
		}
	}

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"data":    aiAnalysis,
	})
}

// AnalyzeTicketById handles POST /api/tickets/:id/analyze
// This creates an async analysis task and returns immediately
func (h *AIHandler) AnalyzeTicketById(c *gin.Context) {
	ticketID := c.Param("id")

	// Check if analysis already exists
	existing, err := h.analysisRepo.GetByTicketID(c.Request.Context(), ticketID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check analysis status"})
		return
	}

	// If already analyzing, return current status
	if existing != nil && existing.Status == models.AnalysisStatusAnalyzing {
		c.JSON(http.StatusOK, gin.H{
			"status":  "analyzing",
			"message": "Analysis is already in progress",
		})
		return
	}

	// If completed, return existing result
	if existing != nil && existing.Status == models.AnalysisStatusCompleted {
		c.JSON(http.StatusOK, gin.H{
			"status":     "completed",
			"rootCause":  existing.RootCause,
			"solution":   existing.Solution,
			"prevention": existing.Prevention,
		})
		return
	}

	// Get ticket details
	ticket, err := h.ticketClient.GetTicket(c.Request.Context(), &ticketv1.GetTicketRequest{Id: ticketID})
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get ticket"})
		return
	}
	if ticket == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Ticket not found"})
		return
	}

	// Create analysis record
	analysis := &models.TicketAnalysis{
		ID:       uuid.New().String(),
		TicketID: ticketID,
		Status:   models.AnalysisStatusAnalyzing,
	}

	if existing != nil {
		// Update existing record
		if err := h.analysisRepo.UpdateStatus(c.Request.Context(), ticketID, models.AnalysisStatusAnalyzing); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update analysis status"})
			return
		}
	} else {
		// Create new record
		if err := h.analysisRepo.Create(c.Request.Context(), analysis); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create analysis record"})
			return
		}
	}

	// Start async analysis in background
	go h.performAsyncAnalysis(ticketID, ticket)

	// Return immediately
	c.JSON(http.StatusAccepted, gin.H{
		"status":  "analyzing",
		"message": "Analysis started, please check status",
	})
}

// performAsyncAnalysis runs AI analysis in background
func (h *AIHandler) performAsyncAnalysis(ticketID string, ticket *ticketv1.Ticket) {
	ctx := context.Background()

	// Parse and format description
	formattedDesc := formatDescription(ticket.Description)

	// Build analysis prompt
	prompt := fmt.Sprintf(`As a DevOps expert, analyze the following ticket and provide a structured solution.

**Ticket Information**
- Title: %s
- Description: %s
- Priority: %s
- Type: %s

Please return the analysis result in JSON format with the following fields:
{
  "rootCause": "Root cause analysis (detailed explanation in English)",
  "solution": "Solution (step-by-step in English, use numbered list with detailed steps)",
  "prevention": "Preventive measures (how to avoid in the future, use numbered list)"
}

IMPORTANT: All content must be in English. Use markdown formatting for better readability.`, ticket.Title, formattedDesc, ticket.Priority, ticket.Type)

	// Call OpenAI
	result, err := h.openaiClient.Analyze(ctx, prompt)
	if err != nil {
		// Update as failed
		h.analysisRepo.UpdateError(ctx, ticketID, err.Error())
		return
	}

	// Parse JSON response
	var analysisResult struct {
		RootCause  string `json:"rootCause"`
		Solution   string `json:"solution"`
		Prevention string `json:"prevention"`
	}

	// Try to extract JSON from response
	jsonStr := extractJSON(result)
	if err := json.Unmarshal([]byte(jsonStr), &analysisResult); err != nil {
		// If parsing fails, use raw text
		analysisResult.RootCause = result
		analysisResult.Solution = "Please refer to the analysis above"
		analysisResult.Prevention = "N/A"
	}

	// Update analysis result
	h.analysisRepo.UpdateResult(ctx, ticketID,
		analysisResult.RootCause,
		analysisResult.Solution,
		analysisResult.Prevention)
}

// formatDescription formats the description with markdown
func formatDescription(desc string) string {
	// Already formatted, return as is
	if strings.Contains(desc, "**Alert Details**") {
		return desc
	}
	return desc
}

// extractJSON extracts JSON object from text
func extractJSON(text string) string {
	// Find first { and last }
	start := strings.Index(text, "{")
	end := strings.LastIndex(text, "}")
	if start >= 0 && end > start {
		return text[start : end+1]
	}
	// Return as is if no JSON found
	return fmt.Sprintf(`{"rootCause":"%s","solution":"Please refer to analysis content","prevention":"N/A"}`, text)
}

// GetAnalysisStatus handles GET /api/tickets/:id/analysis
func (h *AIHandler) GetAnalysisStatus(c *gin.Context) {
	ticketID := c.Param("id")

	analysis, err := h.analysisRepo.GetByTicketID(c.Request.Context(), ticketID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get analysis status"})
		return
	}

	if analysis == nil {
		c.JSON(http.StatusOK, gin.H{"status": "pending"})
		return
	}

	response := gin.H{
		"status": string(analysis.Status),
	}

	if analysis.Status == models.AnalysisStatusCompleted {
		response["rootCause"] = analysis.RootCause
		response["solution"] = analysis.Solution
		response["prevention"] = analysis.Prevention
		response["completedAt"] = analysis.CompletedAt
	} else if analysis.Status == models.AnalysisStatusFailed {
		response["error"] = analysis.ErrorMsg
	}

	c.JSON(http.StatusOK, response)
}
